home *** CD-ROM | disk | FTP | other *** search
/ PC World 2007 December / PCWorld_2007-12_cd.bin / v cisle / htttrack / httrack-3.41-3.exe / {app} / src / proxy / store.c < prev    next >
C/C++ Source or Header  |  2006-10-15  |  69KB  |  2,248 lines

  1. /* ------------------------------------------------------------ */
  2. /*
  3. HTTrack Website Copier, Offline Browser for Windows and Unix
  4. Copyright (C) Xavier Roche and other contributors
  5.  
  6. This program is free software; you can redistribute it and/or
  7. modify it under the terms of the GNU General Public License
  8. as published by the Free Software Foundation; either version 2
  9. of the License, or any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
  19.  
  20. Please visit our Website: http://www.httrack.com
  21. */
  22.  
  23. /* Parts (inside ARC format routines) by Lars Clausen (lc@statsbiblioteket.dk) */
  24.  
  25. /* ------------------------------------------------------------ */
  26. /* File: Cache manager for ProxyTrack                           */
  27. /* Author: Xavier Roche                                         */
  28. /* ------------------------------------------------------------ */
  29.  
  30. #include <stdio.h>
  31. #include <stdlib.h>
  32. #include <string.h>
  33. #include <time.h>
  34.  
  35. /* Locking */
  36. #ifdef _WIN32
  37. #include <process.h>    /* _beginthread, _endthread */
  38. #else
  39. #include <pthread.h>
  40. #endif
  41.  
  42. #include "htsglobal.h"
  43.  
  44. #define HTS_INTERNAL_BYTECODE
  45. #include "htsinthash.h"
  46. #include "htsmd5.h"
  47. #undef HTS_INTERNAL_BYTECODE
  48. #include "../minizip/mztools.h"
  49. #include "../minizip/zip.h"
  50.  
  51. #include "htscore.h"
  52. #include "htsback.h"
  53.  
  54. #include "store.h"
  55. #include "proxystrings.h"
  56. #include "proxytrack.h"
  57.  
  58. /* Unlocked functions */
  59.  
  60. static int PT_LookupCache__New_u(PT_Index index, const char* url);
  61. static PT_Element PT_ReadCache__New_u(PT_Index index, const char* url, int flags);
  62.  
  63. static int PT_LookupCache__Old_u(PT_Index index, const char* url);
  64. static PT_Element PT_ReadCache__Old_u(PT_Index index, const char* url, int flags);
  65.  
  66. static int PT_LookupCache__Arc_u(PT_Index index, const char* url);
  67. static PT_Element PT_ReadCache__Arc_u(PT_Index index, const char* url, int flags);
  68.  
  69. /* Locking */
  70.  
  71. #ifdef _WIN32
  72. void MutexInit(PT_Mutex *pMutex) {
  73.     *pMutex = CreateMutex(NULL,FALSE,NULL);
  74. }
  75.  
  76. void MutexLock(PT_Mutex *pMutex) {
  77.     WaitForSingleObject(*pMutex, INFINITE);
  78. }
  79.  
  80. void MutexUnlock(PT_Mutex *pMutex) {
  81.     ReleaseMutex(*pMutex);
  82. }
  83.  
  84. void MutexFree(PT_Mutex *pMutex) {
  85.     CloseHandle(*pMutex);
  86.     *pMutex = NULL;
  87. }
  88. #else
  89. void MutexInit(PT_Mutex *pMutex) {
  90.     (void) pthread_mutex_init(pMutex, 0);
  91. }
  92.  
  93. void MutexLock(PT_Mutex *pMutex) {
  94.     pthread_mutex_lock(pMutex);
  95. }
  96.  
  97. void MutexUnlock(PT_Mutex *pMutex) {
  98.     pthread_mutex_unlock(pMutex);
  99. }
  100.  
  101. void MutexFree(PT_Mutex *pMutex) {
  102.     pthread_mutex_destroy(pMutex);
  103. }
  104. #endif
  105.  
  106. /* Indexes */
  107.  
  108. typedef struct _PT_Index__New _PT_Index__New;
  109. typedef struct _PT_Index__Old _PT_Index__Old;
  110. typedef struct _PT_Index__Arc _PT_Index__Arc;
  111. typedef struct _PT_Index_Functions _PT_Index_Functions;
  112.  
  113. typedef struct _PT_Index__New *PT_Index__New;
  114. typedef struct _PT_Index__Old *PT_Index__Old;
  115. typedef struct _PT_Index__Arc *PT_Index__Arc;
  116. typedef struct _PT_Index_Functions *PT_Index_Functions;
  117.  
  118. enum {
  119.     PT_CACHE_UNDEFINED = -1,
  120.     PT_CACHE_MIN = 0,
  121.     PT_CACHE__NEW = PT_CACHE_MIN,
  122.     PT_CACHE__OLD,
  123.     PT_CACHE__ARC,
  124.     PT_CACHE_MAX = PT_CACHE__ARC
  125. };
  126.  
  127. static int PT_LoadCache__New(PT_Index index, const char *filename);
  128. static void PT_Index_Delete__New(PT_Index *pindex);
  129. static PT_Element PT_ReadCache__New(PT_Index index, const char* url, int flags);
  130. static int PT_LookupCache__New(PT_Index index, const char* url);
  131. static int PT_SaveCache__New(PT_Indexes indexes, const char *filename);
  132. /**/
  133. static int PT_LoadCache__Old(PT_Index index, const char *filename);
  134. static void PT_Index_Delete__Old(PT_Index *pindex);
  135. static PT_Element PT_ReadCache__Old(PT_Index index, const char* url, int flags);
  136. static int PT_LookupCache__Old(PT_Index index, const char* url);
  137. /**/
  138. static int PT_LoadCache__Arc(PT_Index index, const char *filename);
  139. static void PT_Index_Delete__Arc(PT_Index *pindex);
  140. static PT_Element PT_ReadCache__Arc(PT_Index index, const char* url, int flags);
  141. static int PT_LookupCache__Arc(PT_Index index, const char* url);
  142. static int PT_SaveCache__Arc(PT_Indexes indexes, const char *filename);
  143.  
  144. struct _PT_Index_Functions {
  145.     /* Mandatory services */
  146.     int (*PT_LoadCache)(PT_Index index, const char *filename);
  147.     void (*PT_Index_Delete)(PT_Index *pindex);
  148.     PT_Element (*PT_ReadCache)(PT_Index index, const char* url, int flags);
  149.     int (*PT_LookupCache)(PT_Index index, const char* url);
  150.  
  151.     /* Optional services */
  152.     int (*PT_SaveCache)(PT_Indexes indexes, const char *filename);
  153. };
  154.  
  155. static _PT_Index_Functions _IndexFuncts[] = {
  156.   { PT_LoadCache__New, PT_Index_Delete__New, PT_ReadCache__New, PT_LookupCache__New, PT_SaveCache__New },
  157.   { PT_LoadCache__Old, PT_Index_Delete__Old, PT_ReadCache__Old, PT_LookupCache__Old, NULL },
  158.   { PT_LoadCache__Arc, PT_Index_Delete__Arc, PT_ReadCache__Arc, PT_LookupCache__Arc, PT_SaveCache__Arc },
  159.     { NULL, NULL, NULL, NULL }
  160. };
  161.  
  162. #define PT_INDEX_COMMON_STRUCTURE \
  163.     time_t timestamp;                                \
  164.     inthash hash;                                        \
  165.     char startUrl[1024]
  166.  
  167. struct _PT_Index__New {
  168.     PT_INDEX_COMMON_STRUCTURE;
  169.     char path[1024];        /* either empty, or must include ending / */
  170.     int fixedPath;
  171.     int safeCache;
  172.     unzFile zFile;
  173.     PT_Mutex zFileLock;
  174. };
  175.  
  176. struct _PT_Index__Old {
  177.     PT_INDEX_COMMON_STRUCTURE;
  178.     char filenameDat[1024];
  179.     char filenameNdx[1024];
  180.     FILE *dat,*ndx;
  181.     PT_Mutex fileLock;
  182.     int version;
  183.     char lastmodified[1024];
  184.     char path[1024];        /* either empty, or must include ending / */
  185.     int fixedPath;
  186.     int safeCache;
  187. };
  188.  
  189. struct _PT_Index__Arc {
  190.     PT_INDEX_COMMON_STRUCTURE;
  191.     FILE *file;
  192.     PT_Mutex fileLock;
  193.     int version;
  194.     char lastmodified[1024];
  195.     char line[2048];
  196.     char filenameIndexBuff[2048];
  197. };
  198.  
  199. struct _PT_Index {
  200.     int type;
  201.     union {
  202.         _PT_Index__New formatNew;
  203.         _PT_Index__Old formatOld;
  204.         _PT_Index__Arc formatArc;
  205.         struct {
  206.             PT_INDEX_COMMON_STRUCTURE;
  207.         } common;
  208.     } slots;
  209. };
  210.  
  211. struct _PT_Indexes {
  212.     inthash cil;
  213.     struct _PT_Index **index;
  214.     int index_size;
  215. };
  216.  
  217. struct _PT_CacheItem {
  218.     time_t lastUsed;
  219.     size_t size;
  220.     void* data;
  221. };
  222.  
  223. struct _PT_Cache {
  224.     inthash index;
  225.     size_t maxSize;
  226.     size_t totalSize;
  227.     int count;
  228. };
  229.  
  230. PT_Indexes PT_New(void) {
  231.     PT_Indexes index = (PT_Indexes) calloc(sizeof(_PT_Indexes), 1);
  232.     index->cil = inthash_new(127);
  233.     index->index_size = 0;
  234.     index->index = NULL;
  235.     return index;
  236. }
  237.  
  238. void PT_Delete(PT_Indexes index) {
  239.     if (index != NULL) {
  240.         inthash_delete(&index->cil);
  241.         free(index);
  242.     }
  243. }
  244.  
  245. int PT_RemoveIndex(PT_Indexes index, int indexId) {
  246.     return 0;
  247. }
  248.  
  249. #define assertf(exp)
  250.  
  251. static int binput(char* buff,char* s,int max) {
  252.   int count = 0;
  253.   int destCount = 0;
  254.  
  255.   // Note: \0 will return 1
  256.   while(destCount < max && buff[count] != '\0' && buff[count] != '\n') {
  257.     if (buff[count] != '\r') {
  258.       s[destCount++] = buff[count];
  259.     }
  260.         count++;
  261.   }
  262.   s[destCount] = '\0';
  263.  
  264.   // then return the supplemental jump offset
  265.   return count + 1;
  266. }
  267.  
  268. static time_t file_timestamp(const char* file) {
  269.   struct stat buf;
  270.   if (stat(file, &buf) == 0) {
  271.     time_t tt = buf.st_mtime;
  272.         if (tt != (time_t) 0 && tt != (time_t) -1) {
  273.             return tt;
  274.         }
  275.   }
  276.   return (time_t) 0;
  277. }
  278.  
  279. static int PT_Index_Check__(PT_Index index, const char* file, int line) {
  280.     if (index == NULL)
  281.         return 0;
  282.     if (index->type >= PT_CACHE_MIN && index->type <= PT_CACHE_MAX)
  283.         return 1;
  284.     CRITICAL_("index corrupted in memory", file, line);
  285.     return 0;
  286. }
  287. #define SAFE_INDEX(index) PT_Index_Check__(index, __FILE__, __LINE__)
  288.  
  289.  
  290. /* ------------------------------------------------------------ */
  291. /* Generic cache dispatch                                       */
  292. /* ------------------------------------------------------------ */
  293.  
  294. void PT_Index_Delete(PT_Index *pindex) {
  295.     if (pindex != NULL && (*pindex) != NULL) {
  296.         PT_Index index = *pindex;
  297.         if (SAFE_INDEX(index)) {
  298.             _IndexFuncts[index->type].PT_Index_Delete(pindex);
  299.         }
  300.         free(index);
  301.         *pindex = NULL;
  302.     }
  303. }
  304.  
  305. static void PT_Index_Delete__New(PT_Index *pindex) {
  306.     if (pindex != NULL && (*pindex) != NULL) {
  307.         PT_Index__New index = &(*pindex)->slots.formatNew;
  308.         if (index->zFile != NULL) {
  309.             unzClose(index->zFile);
  310.             index->zFile = NULL;
  311.         }
  312.         if (index->hash != NULL) {
  313.             inthash_delete(&index->hash);
  314.             index->hash = NULL;
  315.         }
  316.         MutexFree(&index->zFileLock);
  317.     }
  318. }
  319.  
  320. static void PT_Index_Delete__Old(PT_Index *pindex) {
  321.     if (pindex != NULL && (*pindex) != NULL) {
  322.         PT_Index__Old index = &(*pindex)->slots.formatOld;
  323.         if (index->dat != NULL) {
  324.             fclose(index->dat);
  325.         }
  326.         if (index->ndx != NULL) {
  327.             fclose(index->ndx);
  328.         }
  329.         if (index->hash != NULL) {
  330.             inthash_delete(&index->hash);
  331.             index->hash = NULL;
  332.         }
  333.         MutexFree(&index->fileLock);
  334.     }
  335. }
  336.  
  337. static void PT_Index_Delete__Arc(PT_Index *pindex) {
  338.     if (pindex != NULL && (*pindex) != NULL) {
  339.         PT_Index__Arc index = &(*pindex)->slots.formatArc;
  340.         if (index->file != NULL) {
  341.             fclose(index->file);
  342.         }
  343.         MutexFree(&index->fileLock);
  344.     }
  345. }
  346.  
  347. int PT_AddIndex(PT_Indexes indexes, const char *path) {
  348.     PT_Index index = PT_LoadCache(path);
  349.     if (index != NULL) {
  350.         int ret = PT_IndexMerge(indexes, &index);
  351.         if (index != NULL) {
  352.             PT_Index_Delete(&index);
  353.         }
  354.         return ret;
  355.     }
  356.     return -1;
  357. }
  358.  
  359. PT_Element PT_Index_HTML_BuildRootInfo(PT_Indexes indexes) {
  360.     if (indexes != NULL) {
  361.         PT_Element elt = PT_ElementNew();
  362.         int i;
  363.         String html = STRING_EMPTY;
  364.         StringClear(html);
  365.         StringCat(html, 
  366.             "<html>"
  367.             PROXYTRACK_COMMENT_HEADER
  368.             DISABLE_IE_FRIENDLY_HTTP_ERROR_MESSAGES
  369.             "<head>\r\n"
  370.             "<title>ProxyTrack " PROXYTRACK_VERSION " Catalog</title>"
  371.             "</head>\r\n"
  372.             "<body>\r\n"
  373.             "<h3>Available sites in this cache:</h3><br />"
  374.             "<br />"
  375.             );
  376.         StringCat(html, "<ul>\r\n");
  377.         for(i = 0 ; i < indexes->index_size ; i++) {
  378.             if (indexes->index[i] != NULL
  379.                 && indexes->index[i]->slots.common.startUrl[0] != '\0')
  380.             {
  381.                 const char * url = indexes->index[i]->slots.common.startUrl;
  382.                 StringCat(html, "<li>\r\n");
  383.                 StringCat(html, "<a href=\"");
  384.                 StringCat(html, url);
  385.                 StringCat(html, "\">");
  386.                 StringCat(html, url);
  387.                 StringCat(html, "</a>\r\n");
  388.                 StringCat(html, "</li>\r\n");
  389.             }
  390.         }
  391.         StringCat(html, "</ul>\r\n");
  392.         StringCat(html, "</body></html>\r\n");
  393.         elt->size = StringLength(html);
  394.         elt->adr = StringAcquire(&html);
  395.         elt->statuscode = HTTP_OK;
  396.         strcpy(elt->charset, "iso-8859-1");
  397.         strcpy(elt->contenttype, "text/html");
  398.         strcpy(elt->msg, "OK");
  399.         StringFree(html);
  400.         return elt;
  401.     }
  402.     return NULL;
  403. }
  404.  
  405. static char* strchr_stop(char* str, char c, char stop) {
  406.     for( ; *str != 0 && *str != stop && *str != c ; str++);
  407.     if (*str == c)
  408.         return str;
  409.     return NULL;
  410. }
  411.  
  412. char ** PT_Enumerate(PT_Indexes indexes, const char *url, int subtree) {
  413.     // should be cached!
  414.     if (indexes != NULL && indexes->cil != NULL) {
  415.         unsigned int urlSize;
  416.         String list = STRING_EMPTY;
  417.         String listindexes = STRING_EMPTY;
  418.         String subitem = STRING_EMPTY;
  419.         unsigned int listCount = 0;
  420.         struct_inthash_enum en = inthash_enum_new(indexes->cil);
  421.         inthash_chain* chain;
  422.         inthash hdupes = NULL;
  423.         if (!subtree)
  424.             hdupes= inthash_new(127);
  425.         StringClear(list);
  426.         StringClear(listindexes);
  427.         StringClear(subitem);
  428.         if (strncmp(url, "http://", 7) == 0)
  429.             url += 7;
  430.         urlSize = (unsigned int) strlen(url);
  431.         while((chain = inthash_enum_next(&en))) {
  432.             long int index = (long int)chain->value.intg;
  433.             if (urlSize == 0 || strncmp(chain->name, url, urlSize) == 0) {
  434.                 if (index >= 0 && index < indexes->index_size) {
  435.                     char * item = chain->name + urlSize;
  436.                     if (*item == '/')
  437.                         item++;
  438.                     {
  439.                         char * pos = subtree ? 0 : strchr_stop(item, '/', '?');
  440.                         unsigned int len = pos ? (unsigned int)( pos - item ) : (unsigned int)strlen(item);
  441.                         if (len > 0 /* default document */ || *item == 0) {
  442.                             int isFolder = ( item[len] == '/' );
  443.                             StringClear(subitem);
  444.                             if (len > 0)
  445.                                 StringMemcat(subitem, item, len);
  446.                             if (len == 0 || !inthash_exists(hdupes, StringBuff(subitem))) {
  447.                                 char* ptr = NULL;
  448.                                 ptr += StringLength(list);
  449.                                 if (len > 0)
  450.                                     StringCat(list, StringBuff(subitem));
  451.                                 if (isFolder)
  452.                                     StringCat(list, "/");
  453.                                 StringMemcat(list, "\0", 1);        /* NULL terminated strings */
  454.                                 StringMemcat(listindexes, &ptr, sizeof(ptr));
  455.                                 listCount++;
  456.                                 inthash_write(hdupes, StringBuff(subitem), 0);
  457.                             }
  458.                         }
  459.                     }
  460.                 } else {
  461.                     CRITICAL("PT_Enumerate:Corrupted central index locator");
  462.                 }
  463.             }
  464.         }
  465.         StringFree(subitem);
  466.         inthash_delete(&hdupes);
  467.         if (listCount > 0) {
  468.             unsigned int i;
  469.             void* blk;
  470.             char *nullPointer = NULL;
  471.             char* startStrings;
  472.             /* NULL terminated index */
  473.             StringMemcat(listindexes, &nullPointer, sizeof(nullPointer));
  474.             /* start of all strings (index) */
  475.             startStrings = nullPointer + StringLength(listindexes);
  476.             /* copy list of URLs after indexes */
  477.             StringMemcat(listindexes, StringBuff(list), StringLength(list));
  478.             /* ---- no reallocation beyond this point (fixed addresses) ---- */
  479.             /* start of all strings (pointer) */
  480.             startStrings = (startStrings - nullPointer) + StringBuffRW(listindexes);
  481.             /* transform indexes into references */
  482.             for(i = 0 ; i < listCount ; i++) {
  483.                 char *ptr = NULL;
  484.                 unsigned int ndx;
  485.                 memcpy(&ptr, &StringBuff(listindexes)[i*sizeof(char*)], sizeof(char*));
  486.                 ndx = (unsigned int) (ptr - nullPointer);
  487.                 ptr = startStrings + ndx;
  488.                 memcpy(&StringBuffRW(listindexes)[i*sizeof(char*)], &ptr, sizeof(char*));
  489.             }
  490.             blk = StringAcquire(&listindexes);
  491.             StringFree(list);
  492.             StringFree(listindexes);
  493.             return (char **)blk;
  494.         }
  495.     }
  496.     return NULL;
  497. }
  498.  
  499. void PT_Enumerate_Delete(char ***plist) {
  500.     if (plist != NULL && *plist != NULL) {
  501.         free(*plist);
  502.         *plist = NULL;
  503.     }
  504. }
  505.  
  506. static int PT_GetType(const char *filename) {
  507.     char * dot = strrchr(filename, '.');
  508.     if (dot != NULL) {
  509.         if (strcasecmp(dot, ".zip") == 0) {
  510.             return PT_CACHE__NEW;
  511.         } else if (strcasecmp(dot, ".ndx") == 0 || strcasecmp(dot, ".dat") == 0) {
  512.             return PT_CACHE__OLD;
  513.         } else if (strcasecmp(dot, ".arc") == 0) {
  514.             return PT_CACHE__ARC;
  515.         }
  516.     }
  517.     return PT_CACHE_UNDEFINED;
  518. }
  519.  
  520. PT_Index PT_LoadCache(const char *filename) {
  521.     int type = PT_GetType(filename);
  522.     if (type != PT_CACHE_UNDEFINED) {
  523.         PT_Index index = calloc(sizeof(_PT_Index), 1);
  524.         if (index != NULL) {
  525.             index->type = type;
  526.             index->slots.common.timestamp = (time_t) time(NULL);
  527.             index->slots.common.startUrl[0] = '\0';
  528.             index->slots.common.hash = inthash_new(8191);
  529.             if (!_IndexFuncts[type].PT_LoadCache(index, filename)) {
  530.                 DEBUG("reading httrack cache (format #%d) %s : error" _ type _ filename );
  531.                 free(index);
  532.                 index = NULL;
  533.                 return NULL;
  534.             } else {
  535.                 DEBUG("reading httrack cache (format #%d) %s : success" _ type _ filename );
  536.             }
  537.             /* default starting URL is the first hash entry */
  538.             if (index->slots.common.startUrl[0] == '\0') {
  539.                 struct_inthash_enum en = inthash_enum_new(index->slots.common.hash);
  540.                 inthash_chain* chain;
  541.                 chain = inthash_enum_next(&en);
  542.                 if (chain != NULL
  543.                     && strstr(chain->name, "/robots.txt") != NULL) 
  544.                 {
  545.                     chain = inthash_enum_next(&en);
  546.                 }
  547.                 if (chain != NULL) {
  548.                     if (!link_has_authority(chain->name))
  549.                         strcat(index->slots.common.startUrl, "http://");
  550.                     strcat(index->slots.common.startUrl, chain->name);
  551.                 }
  552.             }
  553.         }
  554.         return index;
  555.     }
  556.     return NULL;
  557. }
  558.  
  559.  
  560. static long int filesize(const char* filename) {
  561.   struct stat st;
  562.   memset(&st, 0, sizeof(st));
  563.   if (stat(filename, &st) == 0) {
  564.         return (long int)st.st_size;
  565.   }
  566.   return -1;
  567.  
  568. int PT_LookupCache(PT_Index index, const char* url) {
  569.     if (index != NULL && SAFE_INDEX(index)) {
  570.         return _IndexFuncts[index->type].PT_LookupCache(index, url);
  571.     }
  572.     return 0;
  573. }
  574.  
  575. int PT_SaveCache(PT_Indexes indexes, const char *filename) {
  576.     int type = PT_GetType(filename);
  577.     if (type != PT_CACHE_UNDEFINED) {
  578.         if (_IndexFuncts[type].PT_SaveCache != NULL) {
  579.             int ret = _IndexFuncts[type].PT_SaveCache(indexes, filename);
  580.             if (ret == 0) {
  581.                 (void) set_filetime_time_t(filename, PT_GetTimeIndex(indexes));
  582.                 return 0;
  583.             }
  584.         }
  585.     }
  586.     return -1;
  587. }
  588.  
  589. int PT_EnumCache(PT_Indexes indexes, int (*callback)(void *, const char *url, PT_Element), void *arg) {
  590.     if (indexes != NULL && indexes->cil != NULL) {
  591.         struct_inthash_enum en = inthash_enum_new(indexes->cil);
  592.         inthash_chain* chain;
  593.         while((chain = inthash_enum_next(&en))) {
  594.             const long int index_id = (long int)chain->value.intg;
  595.             const char *const url = chain->name;
  596.             if (index_id >= 0 && index_id <= indexes->index_size) {
  597.                 PT_Element item =  PT_ReadCache(indexes->index[index_id], url, FETCH_HEADERS | FETCH_BODY);
  598.                 if (item != NULL) {
  599.                     int ret = callback(arg, url, item);
  600.                     PT_Element_Delete(&item);
  601.                     if (ret != 0)
  602.                         return ret;
  603.                 }
  604.             } else {
  605.                 CRITICAL("PT_ReadCache:Corrupted central index locator");
  606.                 return -1;
  607.             }
  608.         }
  609.     }
  610.     return 0;
  611. }
  612.  
  613. time_t PT_Index_Timestamp(PT_Index index) {
  614.     return index->slots.common.timestamp;
  615. }
  616.  
  617. static int PT_LookupCache__New(PT_Index index, const char* url) {
  618.     int retCode;
  619.     MutexLock(&index->slots.formatNew.zFileLock);
  620.     {
  621.         retCode = PT_LookupCache__New_u(index, url);
  622.     }
  623.     MutexUnlock(&index->slots.formatNew.zFileLock);
  624.     return retCode;
  625. }
  626.  
  627. static int PT_LookupCache__New_u(PT_Index index_, const char* url) {
  628.     if (index_ != NULL) {
  629.         PT_Index__New index = &index_->slots.formatNew;
  630.         if (index->hash != NULL && index->zFile != NULL && url != NULL && *url != 0) {
  631.             int hash_pos_return;
  632.             if (strncmp(url, "http://", 7) == 0)
  633.                 url += 7;
  634.             hash_pos_return = inthash_read(index->hash, url, NULL);
  635.             if (hash_pos_return)
  636.                 return 1;
  637.         }
  638.     }
  639.     return 0;
  640. }
  641.  
  642. int PT_IndexMerge(PT_Indexes indexes, PT_Index *pindex)
  643. {
  644.     if (pindex != NULL && *pindex != NULL && (*pindex)->slots.common.hash != NULL
  645.         && indexes != NULL) 
  646.     {
  647.         PT_Index index = *pindex;
  648.         struct_inthash_enum en = inthash_enum_new(index->slots.common.hash);
  649.         inthash_chain* chain;
  650.         int index_id = indexes->index_size++;
  651.         int nMerged = 0;
  652.         if ((indexes->index = realloc(indexes->index, sizeof(struct _PT_Index)*indexes->index_size)) != NULL) {
  653.             indexes->index[index_id] = index;
  654.             *pindex = NULL;
  655.             while((chain = inthash_enum_next(&en)) != NULL) {
  656.                 const char * url = chain->name;
  657.                 if (url != NULL && url[0] != '\0') {
  658.                     intptr_t previous_index_id = 0;
  659.                     if (inthash_read(indexes->cil, url, &previous_index_id)) {
  660.                         if (previous_index_id >= 0 && previous_index_id < indexes->index_size) {
  661.                             if (indexes->index[previous_index_id]->slots.common.timestamp > index->slots.common.timestamp)            // existing entry is newer
  662.                                 break;
  663.                         } else {
  664.                             CRITICAL("PT_IndexMerge:Corrupted central index locator");
  665.                         }
  666.                     }
  667.                     inthash_write(indexes->cil, chain->name, index_id);
  668.                     nMerged++;
  669.                 }
  670.             }
  671.         } else {
  672.             CRITICAL("PT_IndexMerge:Memory exhausted");
  673.         }
  674.         return nMerged;
  675.     }
  676.     return -1;
  677. }
  678.  
  679. void PT_Element_Delete(PT_Element *pentry) {
  680.     if (pentry != NULL) {
  681.         PT_Element entry = *pentry;
  682.         if (entry != NULL) {
  683.             if (entry->adr != NULL) {
  684.                 free(entry->adr);
  685.                 entry->adr = NULL;
  686.             }
  687.             if (entry->headers != NULL) {
  688.                 free(entry->headers);
  689.                 entry->headers = NULL;
  690.             }
  691.             if (entry->location != NULL) {
  692.                 free(entry->location);
  693.                 entry->location = NULL;
  694.             }
  695.             free(entry);
  696.         }
  697.         *pentry = NULL;
  698.     }
  699. }
  700.  
  701. PT_Element PT_ReadIndex(PT_Indexes indexes, const char* url, int flags)
  702. {
  703.     if (indexes != NULL) 
  704.     {
  705.         intptr_t index_id;
  706.         if (strncmp(url, "http://", 7) == 0)
  707.             url += 7;
  708.         if (inthash_read(indexes->cil, url, &index_id)) {
  709.             if (index_id >= 0 && index_id <= indexes->index_size) {
  710.                 PT_Element item =  PT_ReadCache(indexes->index[index_id], url, flags);
  711.                 if (item != NULL) {
  712.                     item->indexId = (int) index_id;
  713.                     return item;
  714.                 }
  715.             } else {
  716.                 CRITICAL("PT_ReadCache:Corrupted central index locator");
  717.             }
  718.         }
  719.     }
  720.     return NULL;
  721. }
  722.  
  723. int PT_LookupIndex(PT_Indexes indexes, const char* url) {
  724.     if (indexes != NULL) 
  725.     {
  726.         intptr_t index_id;
  727.         if (strncmp(url, "http://", 7) == 0)
  728.             url += 7;
  729.         if (inthash_read(indexes->cil, url, &index_id)) {
  730.             if (index_id >= 0 && index_id <= indexes->index_size) {
  731.                 return 1;
  732.             } else {
  733.                 CRITICAL("PT_ReadCache:Corrupted central index locator");
  734.             }
  735.         }
  736.     }
  737.     return 0;
  738. }
  739.  
  740. time_t PT_GetTimeIndex(PT_Indexes indexes) {
  741.     if (indexes != NULL && indexes->index_size > 0) 
  742.     {
  743.         int i;
  744.         time_t maxt = indexes->index[0]->slots.common.timestamp;
  745.         for(i = 1 ; i < indexes->index_size ; i++) {
  746.             const time_t currt = indexes->index[i]->slots.common.timestamp;
  747.             if (currt > maxt) {
  748.                 maxt = currt;
  749.             }
  750.         }
  751.         return maxt;
  752.     }
  753.     return (time_t) -1;
  754. }
  755.  
  756. PT_Index PT_GetIndex(PT_Indexes indexes, int indexId) {
  757.     if (indexes != NULL && indexId >= 0 && indexId < indexes->index_size) 
  758.     {
  759.         return indexes->index[indexId];
  760.     }
  761.     return NULL;
  762. }
  763.  
  764. PT_Element PT_ElementNew(void) {
  765.     PT_Element r = NULL;
  766.     if ((r = calloc(sizeof(_PT_Element), 1)) == NULL)
  767.         return NULL;
  768.     r->statuscode=STATUSCODE_INVALID;
  769.     r->indexId = -1;
  770.     return r;
  771. }
  772.  
  773. PT_Element PT_ReadCache(PT_Index index, const char* url, int flags) {
  774.     if (index != NULL && SAFE_INDEX(index)) {
  775.         return _IndexFuncts[index->type].PT_ReadCache(index, url, flags);
  776.     }
  777.     return NULL;
  778. }
  779.  
  780. static PT_Element PT_ReadCache__New(PT_Index index, const char* url, int flags) {
  781.     PT_Element retCode;
  782.     MutexLock(&index->slots.formatNew.zFileLock);
  783.     {
  784.         retCode = PT_ReadCache__New_u(index, url, flags);
  785.     }
  786.     MutexUnlock(&index->slots.formatNew.zFileLock);
  787.     return retCode;
  788. }
  789.  
  790.  
  791. /* ------------------------------------------------------------ */
  792. /* New HTTrack cache (new.zip) format                           */
  793. /* ------------------------------------------------------------ */
  794.  
  795. #define ZIP_FIELD_STRING(headers, headersSize, field, value) do { \
  796.   if ( (value != NULL) && (value)[0] != '\0') { \
  797.     sprintf(headers + headersSize, "%s: %s\r\n", field, (value != NULL) ? (value) : ""); \
  798.     (headersSize) += (int) strlen(headers + headersSize); \
  799.   } \
  800. } while(0)
  801. #define ZIP_FIELD_INT(headers, headersSize, field, value) do { \
  802.   if ( (value != 0) ) { \
  803.     sprintf(headers + headersSize, "%s: "LLintP"\r\n", field, (LLint)(value)); \
  804.     (headersSize) += (int) strlen(headers + headersSize); \
  805.   } \
  806. } while(0)
  807. #define ZIP_FIELD_INT_FORCE(headers, headersSize, field, value) do { \
  808.   sprintf(headers + headersSize, "%s: "LLintP"\r\n", field, (LLint)(value)); \
  809.   (headersSize) += (int) strlen(headers + headersSize); \
  810. } while(0)
  811. #define ZIP_READFIELD_STRING(line, value, refline, refvalue) do { \
  812.   if (line[0] != '\0' && strfield2(line, refline)) { \
  813.     strcpy(refvalue, value); \
  814.     line[0] = '\0'; \
  815.     } \
  816. } while(0)
  817. #define ZIP_READFIELD_INT(line, value, refline, refvalue) do { \
  818.   if (line[0] != '\0' && strfield2(line, refline)) { \
  819.     int intval = 0; \
  820.     sscanf(value, "%d", &intval); \
  821.     (refvalue) = intval; \
  822.     line[0] = '\0'; \
  823.     } \
  824. } while(0)
  825.  
  826. int PT_LoadCache__New(PT_Index index_, const char *filename) {
  827.     if (index_ != NULL && filename != NULL) {
  828.         PT_Index__New index = &index_->slots.formatNew;
  829.         unzFile zFile = index->zFile = unzOpen(filename);
  830.         index->timestamp = file_timestamp(filename);
  831.         MutexInit(&index->zFileLock);
  832.  
  833.         // Opened ?
  834.         if (zFile!=NULL) {
  835.             const char * abpath;
  836.             int slashes;
  837.             inthash hashtable = index->hash;
  838.  
  839.             /* Compute base path for this index - the filename MUST be absolute! */
  840.             for(slashes = 2, abpath = filename + (int)strlen(filename) - 1 
  841.                 ; abpath > filename && ( ( *abpath != '/'&& *abpath != '\\' ) || --slashes > 0)
  842.                 ; abpath--);
  843.             index->path[0] = '\0';
  844.             if (slashes == 0 && *abpath != 0) {
  845.                 int i;
  846.                 strncat(index->path, filename, (int) ( abpath - filename ) + 1 );
  847.                 for(i = 0 ; index->path[i] != 0 ; i++) {
  848.                     if (index->path[i] == '\\') {
  849.                         index->path[i] = '/';
  850.                     }
  851.                 }
  852.             }
  853.  
  854.             /* Ready directory entries */
  855.             if (unzGoToFirstFile(zFile) == Z_OK) {
  856.                 char comment[128];
  857.                 char filename[HTS_URLMAXSIZE * 4];
  858.                 int entries = 0;
  859.                 int firstSeen = 0;
  860.                 memset(comment, 0, sizeof(comment));       // for truncated reads
  861.                 do  {
  862.                     int readSizeHeader = 0;
  863.                     filename[0] = '\0';
  864.                     comment[0] = '\0';
  865.                     if (unzOpenCurrentFile(zFile) == Z_OK) {
  866.                         if (
  867.                             (readSizeHeader = unzGetLocalExtrafield(zFile, comment, sizeof(comment) - 2)) > 0
  868.                             &&
  869.                             unzGetCurrentFileInfo(zFile, NULL, filename, sizeof(filename) - 2, NULL, 0, NULL, 0) == Z_OK
  870.                             ) 
  871.                         {
  872.                             long int pos = (long int) unzGetOffset(zFile);
  873.                             assertf(readSizeHeader < sizeof(comment));
  874.                             comment[readSizeHeader] = '\0';
  875.                             entries++;
  876.                             if (pos > 0) {
  877.                                 int dataincache = 0;    // data in cache ?
  878.                                 char* filenameIndex = filename;
  879.                                 if (strncmp(filenameIndex, "http://", 7) == 0) {
  880.                                     filenameIndex += 7;
  881.                                 }
  882.                                 if (comment[0] != '\0') {
  883.                                     int maxLine = 2;
  884.                                     char* a = comment;
  885.                                     while(*a && maxLine-- > 0) {      // parse only few first lines
  886.                                         char line[1024];
  887.                                         line[0] = '\0';
  888.                                         a+=binput(a, line, sizeof(line) - 2);
  889.                                         if (strncmp(line, "X-In-Cache:", 11) == 0) {
  890.                                             if (strcmp(line, "X-In-Cache: 1") == 0) {
  891.                                                 dataincache = 1;
  892.                                             } else {
  893.                                                 dataincache = 0;
  894.                                             }
  895.                                             break;
  896.                                         }
  897.                                     }
  898.                                 }
  899.                                 if (dataincache)
  900.                                     inthash_add(hashtable, filenameIndex, pos);
  901.                                 else
  902.                                     inthash_add(hashtable, filenameIndex, -pos);
  903.  
  904.                                 /* First link as starting URL */
  905.                                 if (!firstSeen) {
  906.                                     if (strstr(filenameIndex, "/robots.txt") == NULL) {
  907.                                         firstSeen = 1;
  908.                                         if (!link_has_authority(filenameIndex))
  909.                                             strcat(index->startUrl, "http://");
  910.                                         strcat(index->startUrl, filenameIndex);
  911.                                     }
  912.                                 }
  913.                             } else {
  914.                                 fprintf(stderr, "Corrupted cache meta entry #%d"LF, (int)entries);
  915.                             }
  916.                         } else {
  917.                             fprintf(stderr, "Corrupted cache entry #%d"LF, (int)entries);
  918.                         }
  919.                         unzCloseCurrentFile(zFile);
  920.                     } else {
  921.                         fprintf(stderr, "Corrupted cache entry #%d"LF, (int)entries);
  922.                     }
  923.                 } while( unzGoToNextFile(zFile) == Z_OK );
  924.                 return 1;
  925.             } else {
  926.                 inthash_delete(&index->hash);
  927.                 index = NULL;
  928.             }
  929.         } else {
  930.             index = NULL;
  931.         }
  932.     }
  933.     return 0;
  934. }
  935.  
  936. static PT_Element PT_ReadCache__New_u(PT_Index index_, const char* url, int flags)
  937. {
  938.     PT_Index__New index = (PT_Index__New) &index_->slots.formatNew;
  939.   char location_default[HTS_URLMAXSIZE*2];
  940.   char previous_save[HTS_URLMAXSIZE*2];
  941.   char previous_save_[HTS_URLMAXSIZE*2];
  942.     char catbuff[CATBUFF_SIZE];
  943.   intptr_t hash_pos;
  944.   int hash_pos_return;
  945.     PT_Element r = NULL;
  946.     if (index == NULL || index->hash == NULL || index->zFile == NULL || url == NULL || *url == 0)
  947.         return NULL;
  948.     if ((r = PT_ElementNew()) == NULL)
  949.         return NULL;
  950.     location_default[0] = '\0';
  951.     previous_save[0] = previous_save_[0] = '\0';
  952.   memset(r, 0, sizeof(_PT_Element));
  953.   r->location = location_default;
  954.     strcpy(r->location, ""); 
  955.     if (strncmp(url, "http://", 7) == 0)
  956.         url += 7;
  957.   hash_pos_return = inthash_read(index->hash, url, &hash_pos);
  958.  
  959.   if (hash_pos_return) {
  960.     uLong posInZip;
  961.     if (hash_pos > 0) {
  962.       posInZip = (uLong) hash_pos;
  963.     } else {
  964.       posInZip = (uLong) -hash_pos;
  965.     }
  966.         if (unzSetOffset(index->zFile, posInZip) == Z_OK) {
  967.       /* Read header (Max 8KiB) */
  968.       if (unzOpenCurrentFile(index->zFile) == Z_OK) {
  969.         char headerBuff[8192 + 2];
  970.         int readSizeHeader;
  971.         int totalHeader = 0;
  972.         int dataincache = 0;
  973.         
  974.         /* For BIG comments */
  975.         headerBuff[0] 
  976.           = headerBuff[sizeof(headerBuff) - 1] 
  977.           = headerBuff[sizeof(headerBuff) - 2] 
  978.           = headerBuff[sizeof(headerBuff) - 3] = '\0';
  979.  
  980.         if ( (readSizeHeader = unzGetLocalExtrafield(index->zFile, headerBuff, sizeof(headerBuff) - 2)) > 0) 
  981.         {
  982.           int offset = 0;
  983.           char line[HTS_URLMAXSIZE + 2];
  984.           int lineEof = 0;
  985.           headerBuff[readSizeHeader] = '\0';
  986.           do {
  987.             char* value;
  988.             line[0] = '\0';
  989.             offset += binput(headerBuff + offset, line, sizeof(line) - 2);
  990.             if (line[0] == '\0') {
  991.               lineEof = 1;
  992.             }
  993.             value = strchr(line, ':');
  994.             if (value != NULL) {
  995.               *value++ = '\0';
  996.               if (*value == ' ' || *value == '\t') value++;
  997.               ZIP_READFIELD_INT(line, value, "X-In-Cache", dataincache);
  998.               ZIP_READFIELD_INT(line, value, "X-Statuscode", r->statuscode);
  999.               ZIP_READFIELD_STRING(line, value, "X-StatusMessage", r->msg);              // msg
  1000.               ZIP_READFIELD_INT(line, value, "X-Size", r->size);           // size
  1001.               ZIP_READFIELD_STRING(line, value, "Content-Type", r->contenttype);      // contenttype
  1002.               ZIP_READFIELD_STRING(line, value, "X-Charset", r->charset);          // contenttype
  1003.               ZIP_READFIELD_STRING(line, value, "Last-Modified", r->lastmodified);     // last-modified
  1004.               ZIP_READFIELD_STRING(line, value, "Etag", r->etag);             // Etag
  1005.               ZIP_READFIELD_STRING(line, value, "Location", r->location);         // 'location' pour moved
  1006.               ZIP_READFIELD_STRING(line, value, "Content-Disposition", r->cdispo);           // Content-disposition
  1007.               //ZIP_READFIELD_STRING(line, value, "X-Addr", ..);            // Original address
  1008.               //ZIP_READFIELD_STRING(line, value, "X-Fil", ..);            // Original URI filename
  1009.               ZIP_READFIELD_STRING(line, value, "X-Save", previous_save_);           // Original save filename
  1010.                             if (line[0] != '\0') {
  1011.                                 int len = r->headers ? ((int) strlen(r->headers)) : 0;
  1012.                                 int nlen = (int) ( strlen(line) + 2 + strlen(value) + sizeof("\r\n") + 1 );
  1013.                                 r->headers = realloc(r->headers, len + nlen);
  1014.                                 r->headers[len] = '\0';
  1015.                                 strcat(r->headers, line);
  1016.                                 strcat(r->headers, ": ");
  1017.                                 strcat(r->headers, value);
  1018.                                 strcat(r->headers, "\r\n");
  1019.                             }
  1020.             }
  1021.           } while(offset < readSizeHeader && !lineEof);
  1022.           totalHeader = offset;
  1023.  
  1024.                     /* Previous entry */
  1025.                     if (previous_save_[0] != '\0') {
  1026.                         int pathLen = (int) strlen(index->path);
  1027.                         if (pathLen > 0 && strncmp(previous_save_, index->path, pathLen) == 0) {            // old (<3.40) buggy format
  1028.                             strcpy(previous_save, previous_save_);
  1029.                         }
  1030.                         // relative ? (hack)
  1031.                         else if (index->safeCache
  1032.                             || (previous_save_[0] != '/'                                                                    // /home/foo/bar.gif
  1033.                             && ( !isalpha(previous_save_[0]) || previous_save_[1] != ':' ) )    // c:/home/foo/bar.gif
  1034.                             )
  1035.                         {
  1036.                             index->safeCache = 1;
  1037.                             sprintf(previous_save, "%s%s", index->path, previous_save_);
  1038.                         }
  1039.                         // bogus format (includes buggy absolute path)
  1040.                         else {
  1041.                             /* guess previous path */
  1042.                             if (index->fixedPath == 0) {
  1043.                                 const char * start = jump_protocol_and_auth(url);
  1044.                                 const char * end = start ? strchr(start, '/') : NULL;
  1045.                                 int len = (int) (end - start);
  1046.                                 if (start != NULL && end != NULL && len > 0 && len < 128) {
  1047.                                     char piece[128 + 2];
  1048.                                     const char * where;
  1049.                                     piece[0] = '\0';
  1050.                                     strncat(piece, start, len);
  1051.                                     if ((where = strstr(previous_save_, piece)) != NULL) {
  1052.                                         index->fixedPath = (int) (where - previous_save_);        // offset to relative path
  1053.                                     }
  1054.                                 }
  1055.                             }
  1056.                             if (index->fixedPath > 0) {
  1057.                                 int saveLen = (int) strlen(previous_save_);
  1058.                                 if (index->fixedPath < saveLen) {
  1059.                                     sprintf(previous_save, "%s%s", index->path, previous_save_ + index->fixedPath);
  1060.                                 } else {
  1061.                                     sprintf(r->msg, "Bogus fixePath prefix for %s (prefixLen=%d)", previous_save_, (int)index->fixedPath);
  1062.                                     r->statuscode = STATUSCODE_INVALID;
  1063.                                 }
  1064.                             } else {
  1065.                                 sprintf(previous_save, "%s%s", index->path, previous_save_);
  1066.                             }
  1067.                         }
  1068.                     }
  1069.  
  1070.           /* Complete fields */
  1071.           r->adr=NULL;
  1072.           if (r->statuscode != STATUSCODE_INVALID) {            /* Can continue */
  1073.             int ok = 0;
  1074.                        
  1075.             // Court-circuit:
  1076.             // Peut-on stocker le fichier directement sur disque?
  1077.             if (ok) {
  1078.               if (r->msg[0] == '\0') {
  1079.                 strcpy(r->msg,"Cache Read Error : Unexpected error");
  1080.               }
  1081.             } else { // lire en mΘmoire
  1082.               
  1083.               if (!dataincache) {
  1084.                                 /* Read in memory from cache */
  1085.                                 if (flags & FETCH_BODY) {
  1086.                                     if (strnotempty(previous_save)) {
  1087.                                         FILE* fp = fopen(fconv(catbuff,previous_save), "rb");
  1088.                                         if (fp != NULL) {
  1089.                                             r->adr = (char*) malloc(r->size + 4);
  1090.                                             if (r->adr != NULL) {
  1091.                                                 if (r->size > 0 && fread(r->adr, 1,  r->size, fp) != r->size) {
  1092.                           int last_errno = errno;
  1093.                                                     r->statuscode=STATUSCODE_INVALID;
  1094.                                                     sprintf(r->msg,"Read error in cache disk data: %s", strerror(last_errno));
  1095.                                                 }
  1096.                                             } else {
  1097.                                                 r->statuscode=STATUSCODE_INVALID;
  1098.                                                 strcpy(r->msg,"Read error (memory exhausted) from cache");
  1099.                                             }
  1100.                                             fclose(fp);
  1101.                                         } else {
  1102.                                             r->statuscode=STATUSCODE_INVALID;
  1103.                                             sprintf(r->msg, "Read error (can't open '%s') from cache", fconv(catbuff,previous_save));
  1104.                                         }
  1105.                                     } else {
  1106.                                         r->statuscode=STATUSCODE_INVALID;
  1107.                                         strcpy(r->msg,"Cached file name is invalid");
  1108.                                     }
  1109.                                 }
  1110.               } else {
  1111.                                 // lire fichier (d'un coup)
  1112.                                 if (flags & FETCH_BODY) {
  1113.                                     r->adr=(char*) malloc(r->size+1);
  1114.                                     if (r->adr!=NULL) {
  1115.                                         if (unzReadCurrentFile(index->zFile, r->adr, (unsigned int) r->size) != r->size) {  // erreur
  1116.                                             free(r->adr);
  1117.                                             r->adr=NULL;
  1118.                                             r->statuscode=STATUSCODE_INVALID;
  1119.                                             strcpy(r->msg,"Cache Read Error : Read Data");
  1120.                                         } else
  1121.                                             *(r->adr+r->size)='\0';
  1122.                                         //printf(">%s status %d\n",back[p].r->contenttype,back[p].r->statuscode);
  1123.                                     } else {  // erreur
  1124.                                         r->statuscode=STATUSCODE_INVALID;
  1125.                                         strcpy(r->msg,"Cache Memory Error");
  1126.                                     }
  1127.                                 }
  1128.                             }
  1129.             }
  1130.           }    // si save==null, ne rien charger (juste en tΩte)
  1131.         } else {
  1132.           r->statuscode=STATUSCODE_INVALID;
  1133.           strcpy(r->msg,"Cache Read Error : Read Header Data");
  1134.         }
  1135.         unzCloseCurrentFile(index->zFile);
  1136.       } else {
  1137.         r->statuscode=STATUSCODE_INVALID;
  1138.         strcpy(r->msg,"Cache Read Error : Open File");
  1139.       }
  1140.  
  1141.     } else {
  1142.       r->statuscode=STATUSCODE_INVALID;
  1143.       strcpy(r->msg,"Cache Read Error : Bad Offset");
  1144.     }
  1145.   } else {
  1146.     r->statuscode=STATUSCODE_INVALID;
  1147.     strcpy(r->msg,"File Cache Entry Not Found");
  1148.   }
  1149.     if (r->location[0] != '\0') {
  1150.         r->location = strdup(r->location);
  1151.     } else {
  1152.         r->location = NULL;
  1153.     }
  1154.   return r;
  1155. }
  1156.  
  1157. static int PT_SaveCache__New_Fun(void *arg, const char *url, PT_Element element) {
  1158.     zipFile zFileOut = (zipFile) arg;
  1159.     char headers[8192];
  1160.   int headersSize;
  1161.   zip_fileinfo fi;
  1162.     int zErr;
  1163.     const char *url_adr = "";
  1164.     const char *url_fil = "";
  1165.  
  1166.   headers[0] = '\0';
  1167.   headersSize = 0;
  1168.  
  1169.   /* Fields */
  1170.   headers[0] = '\0';
  1171.   headersSize = 0;
  1172.   /* */
  1173.   {
  1174.     char* message;
  1175.     if (strlen(element->msg) < 32) {
  1176.       message = element->msg;
  1177.     } else {
  1178.       message = "(See X-StatusMessage)";
  1179.     }
  1180.     /* 64 characters MAX for first line */
  1181.         sprintf(headers + headersSize, "HTTP/1.%c %d %s\r\n", '1', element->statuscode, element->msg);
  1182.   }
  1183.   headersSize += (int) strlen(headers + headersSize);
  1184.  
  1185.   /* Second line MUST ALWAYS be X-In-Cache */
  1186.   ZIP_FIELD_INT_FORCE(headers, headersSize, "X-In-Cache", 1);
  1187.   ZIP_FIELD_INT(headers, headersSize, "X-StatusCode", element->statuscode);
  1188.   ZIP_FIELD_STRING(headers, headersSize, "X-StatusMessage", element->msg);
  1189.   ZIP_FIELD_INT(headers, headersSize, "X-Size", element->size);           // size
  1190.   ZIP_FIELD_STRING(headers, headersSize, "Content-Type", element->contenttype);      // contenttype
  1191.   ZIP_FIELD_STRING(headers, headersSize, "X-Charset", element->charset);          // contenttype
  1192.   ZIP_FIELD_STRING(headers, headersSize, "Last-Modified", element->lastmodified);     // last-modified
  1193.   ZIP_FIELD_STRING(headers, headersSize, "Etag", element->etag);             // Etag
  1194.   ZIP_FIELD_STRING(headers, headersSize, "Location", element->location);         // 'location' pour moved
  1195.   ZIP_FIELD_STRING(headers, headersSize, "Content-Disposition", element->cdispo);           // Content-disposition
  1196.   ZIP_FIELD_STRING(headers, headersSize, "X-Addr", url_adr);            // Original address
  1197.   ZIP_FIELD_STRING(headers, headersSize, "X-Fil", url_fil);            // Original URI filename
  1198.   ZIP_FIELD_STRING(headers, headersSize, "X-Save", "");           // Original save filename
  1199.   
  1200.   /* Time */
  1201.   memset(&fi, 0, sizeof(fi));
  1202.   if (element->lastmodified[0] != '\0') {
  1203.         struct tm buffer;
  1204.     struct tm* tm_s = convert_time_rfc822(&buffer, element->lastmodified);
  1205.     if (tm_s) {
  1206.       fi.tmz_date.tm_sec = (uInt) tm_s->tm_sec;
  1207.       fi.tmz_date.tm_min = (uInt) tm_s->tm_min;
  1208.       fi.tmz_date.tm_hour = (uInt) tm_s->tm_hour;
  1209.       fi.tmz_date.tm_mday = (uInt) tm_s->tm_mday;
  1210.       fi.tmz_date.tm_mon = (uInt) tm_s->tm_mon;
  1211.       fi.tmz_date.tm_year = (uInt) tm_s->tm_year;
  1212.     }
  1213.   }
  1214.  
  1215.   /* Open file - NOTE: headers in "comment" */
  1216.   if ((zErr = zipOpenNewFileInZip(zFileOut,
  1217.     url,
  1218.     &fi,
  1219.     /* 
  1220.     Store headers in realtime in the local file directory as extra field
  1221.     In case of crash, we'll be able to recover the whole ZIP file by rescanning it
  1222.     */
  1223.     headers,
  1224.     (uInt) strlen(headers),
  1225.     NULL,
  1226.     0,
  1227.     NULL, /* comment */
  1228.     Z_DEFLATED,
  1229.     Z_DEFAULT_COMPRESSION)) != Z_OK)
  1230.   {
  1231.     int zip_zipOpenNewFileInZip_failed = 0;
  1232.     assertf(zip_zipOpenNewFileInZip_failed);
  1233.   }
  1234.   
  1235.     /* Write data in cache */
  1236.     if (element->size > 0 && element->adr != NULL) {
  1237.         if ((zErr = zipWriteInFileInZip(zFileOut, element->adr, (int) element->size)) != Z_OK) {
  1238.             int zip_zipWriteInFileInZip_failed = 0;
  1239.             assertf(zip_zipWriteInFileInZip_failed);
  1240.         }
  1241.     }
  1242.  
  1243.     /* Close */
  1244.     if ((zErr = zipCloseFileInZip(zFileOut)) != Z_OK) {
  1245.         int zip_zipCloseFileInZip_failed = 0;
  1246.     assertf(zip_zipCloseFileInZip_failed);
  1247.   }
  1248.  
  1249.   /* Flush */
  1250.   if ((zErr = zipFlush(zFileOut)) != 0) {
  1251.     int zip_zipFlush_failed = 0;
  1252.     assertf(zip_zipFlush_failed);
  1253.   }
  1254.  
  1255.     return 0;
  1256. }
  1257.  
  1258. static int PT_SaveCache__New(PT_Indexes indexes, const char *filename) {
  1259.     zipFile zFileOut = zipOpen(filename, 0);
  1260.     if (zFileOut != NULL) {
  1261.         int ret = PT_EnumCache(indexes, PT_SaveCache__New_Fun, (void *) zFileOut);
  1262.     zipClose(zFileOut, "Created by HTTrack Website Copier/ProxyTrack "PROXYTRACK_VERSION);
  1263.         zFileOut = NULL;
  1264.         if (ret != 0)
  1265.             (void) unlink(filename);
  1266.         return ret;
  1267.     }
  1268.     return -1;
  1269. }
  1270.  
  1271.  
  1272.  
  1273. /* ------------------------------------------------------------ */
  1274. /* Old HTTrack cache (dat/ndx) format                           */
  1275. /* ------------------------------------------------------------ */
  1276.  
  1277. static int cache_brstr(char* adr,char* s) {
  1278.   int i;
  1279.   int off;
  1280.   char buff[256 + 1];
  1281.   off=binput(adr,buff,256);
  1282.   adr+=off;
  1283.   sscanf(buff,"%d",&i);
  1284.   if (i>0)
  1285.     strncpy(s,adr,i);
  1286.   *(s+i)='\0';
  1287.   off+=i;
  1288.   return off;
  1289. }
  1290.  
  1291. static void cache_rstr(FILE* fp,char* s) {
  1292.   INTsys i;
  1293.   char buff[256+4];
  1294.   linput(fp,buff,256);
  1295.   sscanf(buff,INTsysP,&i);
  1296.   if (i < 0 || i > 32768)    /* error, something nasty happened */
  1297.     i=0;
  1298.   if (i>0) {
  1299.     if ((int) fread(s,1,i,fp) != i) {
  1300.       int fread_cache_failed = 0;
  1301.       assertf(fread_cache_failed);
  1302.     }
  1303.   }
  1304.   *(s+i)='\0';
  1305. }
  1306.  
  1307. static char* cache_rstr_addr(FILE* fp) {
  1308.   INTsys i;
  1309.   char* addr = NULL;
  1310.   char buff[256+4];
  1311.   linput(fp,buff,256);
  1312.   sscanf(buff,"%d",&i);
  1313.   if (i < 0 || i > 32768)    /* error, something nasty happened */
  1314.     i=0;
  1315.   if (i > 0) {
  1316.     addr = malloc(i + 1);
  1317.     if (addr != NULL) {
  1318.       if ((int) fread(addr,1,i,fp) != i) {
  1319.         int fread_cache_failed = 0;
  1320.         assertf(fread_cache_failed);
  1321.       }
  1322.       *(addr+i)='\0';
  1323.     }
  1324.   }
  1325.   return addr;
  1326. }
  1327.  
  1328. static void cache_rint(FILE* fp,int* i) {
  1329.   char s[256];
  1330.   cache_rstr(fp,s);
  1331.   sscanf(s,"%d",i);
  1332. }
  1333.  
  1334. static void cache_rLLint(FILE* fp,unsigned long* i) {
  1335.     int l;
  1336.   char s[256];
  1337.   cache_rstr(fp,s);
  1338.   sscanf(s,"%d",&l);
  1339.     *i = (unsigned long)l;
  1340. }
  1341.  
  1342. static int PT_LoadCache__Old(PT_Index index_, const char *filename) {
  1343.     if (index_ != NULL && filename != NULL) {
  1344.         char * pos = strrchr(filename, '.');
  1345.         PT_Index__Old cache = &index_->slots.formatOld;
  1346.         long int ndxSize;
  1347.         cache->filenameDat[0] = '\0';
  1348.         cache->filenameNdx[0] = '\0';
  1349.         cache->path[0] = '\0';
  1350.  
  1351.         {
  1352.             PT_Index__Old index = cache;
  1353.             const char * abpath;
  1354.             int slashes;
  1355.             /* -------------------- COPY OF THE __New() CODE -------------------- */
  1356.             /* Compute base path for this index - the filename MUST be absolute! */
  1357.             for(slashes = 2, abpath = filename + (int)strlen(filename) - 1 
  1358.                 ; abpath > filename && ( ( *abpath != '/'&& *abpath != '\\' ) || --slashes > 0)
  1359.                 ; abpath--);
  1360.             index->path[0] = '\0';
  1361.             if (slashes == 0 && *abpath != 0) {
  1362.                 int i;
  1363.                 strncat(index->path, filename, (int) ( abpath - filename ) + 1 );
  1364.                 for(i = 0 ; index->path[i] != 0 ; i++) {
  1365.                     if (index->path[i] == '\\') {
  1366.                         index->path[i] = '/';
  1367.                     }
  1368.                 }
  1369.             }
  1370.             /* -------------------- END OF COPY OF THE __New() CODE -------------------- */
  1371.         }
  1372.  
  1373.         /* Index/data filenames */
  1374.         if (pos != NULL) {
  1375.             int nLen = (int) (pos - filename);
  1376.             strncat(cache->filenameDat, filename, nLen);
  1377.             strncat(cache->filenameNdx, filename, nLen);
  1378.             strcat(cache->filenameDat, ".dat");
  1379.             strcat(cache->filenameNdx, ".ndx");
  1380.         }
  1381.         ndxSize = filesize(cache->filenameNdx);
  1382.         cache->timestamp = file_timestamp(cache->filenameDat);
  1383.         cache->dat = fopen(cache->filenameDat, "rb");
  1384.         cache->ndx = fopen(cache->filenameNdx, "rb");
  1385.         if (cache->dat != NULL && cache->ndx != NULL && ndxSize > 0) {
  1386.             char * use = malloc(ndxSize + 1);
  1387.             if (fread(use, 1, ndxSize, cache->ndx) == ndxSize) {
  1388.                 char firstline[256];
  1389.                 char* a=use;
  1390.                 use[ndxSize] = '\0';
  1391.                 a += cache_brstr(a, firstline);
  1392.                 if (strncmp(firstline,"CACHE-",6)==0) {       // Nouvelle version du cache
  1393.                     if (strncmp(firstline,"CACHE-1.",8)==0) {      // Version 1.1x
  1394.                         cache->version=(int)(firstline[8]-'0');           // cache 1.x
  1395.                         if (cache->version <= 5) {
  1396.                             a+=cache_brstr(a,firstline);
  1397.                             strcpy(cache->lastmodified,firstline);
  1398.                         } else {
  1399.                             // fprintf(opt->errlog,"Cache: version 1.%d not supported, ignoring current cache"LF,cache->version);
  1400.                             fclose(cache->dat);
  1401.                             cache->dat=NULL;
  1402.                             free(use);
  1403.                             use=NULL;
  1404.                         }
  1405.                     } else {        // non supportΘ
  1406.                         // fspc(opt->errlog,"error"); fprintf(opt->errlog,"Cache: %s not supported, ignoring current cache"LF,firstline);
  1407.                         fclose(cache->dat);
  1408.                         cache->dat=NULL;
  1409.                         free(use);
  1410.                         use=NULL;
  1411.                     }
  1412.                     /* */
  1413.                 } else {              // Vieille version du cache
  1414.                     /* */
  1415.                     // HTS_LOG(opt,LOG_WARNING); fprintf(opt->log,"Cache: importing old cache format"LF);
  1416.                     cache->version=0;        // cache 1.0
  1417.                     strcpy(cache->lastmodified,firstline); 
  1418.                 }
  1419.  
  1420.                 /* Create hash table for the cache (MUCH FASTER!) */
  1421.                 if (use) {
  1422.                     char line[HTS_URLMAXSIZE*2];
  1423.                     char linepos[256];
  1424.                     int  pos;
  1425.                     int firstSeen = 0;
  1426.                     while ( (a!=NULL) && (a < (use + ndxSize) ) ) {
  1427.                         a=strchr(a+1,'\n');     /* start of line */
  1428.                         if (a) {
  1429.                             a++;
  1430.                             /* read "host/file" */
  1431.                             a+=binput(a,line,HTS_URLMAXSIZE);
  1432.                             a+=binput(a,line+strlen(line),HTS_URLMAXSIZE);
  1433.                             /* read position */
  1434.                             a+=binput(a,linepos,200);
  1435.                             sscanf(linepos,"%d",&pos);
  1436.  
  1437.                             /* Add entry */
  1438.                             inthash_add(cache->hash,line,pos);
  1439.  
  1440.                             /* First link as starting URL */
  1441.                             if (!firstSeen) {
  1442.                                 if (strstr(line, "/robots.txt") == NULL) {
  1443.                                     PT_Index__Old index = cache;
  1444.                                     firstSeen = 1;
  1445.                                     if (!link_has_authority(line))
  1446.                                         strcat(index->startUrl, "http://");
  1447.                                     strcat(index->startUrl, line);
  1448.                                 }
  1449.                             }
  1450.  
  1451.                         }
  1452.                     }
  1453.                     /* Not needed anymore! */
  1454.                     free(use);
  1455.                     use=NULL;
  1456.                     return 1;
  1457.                 }
  1458.             }
  1459.         }
  1460.     }
  1461.     return 0;
  1462. }
  1463.  
  1464. static String DecodeUrl(const char * url) {
  1465.     int i;
  1466.     String s = STRING_EMPTY;
  1467.     StringClear(s);
  1468.     for(i = 0 ; url[i] != '\0' ; i++) {
  1469.         if (url[i] == '+') {
  1470.             StringAddchar(s, ' ');
  1471.         } else if (url[i] == '%') {
  1472.             if (url[i + 1] == '%') {
  1473.                 StringAddchar(s, '%');
  1474.                 i++;
  1475.             } else if (url[i + 1] != 0 && url[i + 2] != 0) {
  1476.                 char tmp[3];
  1477.                 int codepoint = 0;
  1478.                 tmp[0] = url[i + 1];
  1479.                 tmp[1] = url[i + 2];
  1480.                 tmp[2] = 0;
  1481.                 if (sscanf(tmp, "%x", &codepoint) == 1) {
  1482.                     StringAddchar(s, (char)codepoint);
  1483.                 }
  1484.                 i += 2;
  1485.             }
  1486.         } else {
  1487.             StringAddchar(s, url[i]);
  1488.         }
  1489.     }
  1490.     return s;
  1491. }
  1492.  
  1493. static PT_Element PT_ReadCache__Old(PT_Index index, const char* url, int flags) {
  1494.     PT_Element retCode;
  1495.     MutexLock(&index->slots.formatOld.fileLock);
  1496.     {
  1497.         retCode = PT_ReadCache__Old_u(index, url, flags);
  1498.     }
  1499.     MutexUnlock(&index->slots.formatOld.fileLock);
  1500.     return retCode;
  1501. }
  1502.  
  1503. static PT_Element PT_ReadCache__Old_u(PT_Index index_, const char* url, int flags) {
  1504.     PT_Index__Old cache = (PT_Index__Old) &index_->slots.formatOld;
  1505.   intptr_t hash_pos;
  1506.   int hash_pos_return;
  1507.   char location_default[HTS_URLMAXSIZE*2];
  1508.   char previous_save[HTS_URLMAXSIZE*2];
  1509.   char previous_save_[HTS_URLMAXSIZE*2];
  1510.   PT_Element r;
  1511.   int ok=0;
  1512.  
  1513.     if (cache == NULL || cache->hash == NULL || url == NULL || *url == 0)
  1514.         return NULL;
  1515.     if ((r = PT_ElementNew()) == NULL)
  1516.         return NULL;
  1517.     location_default[0] = '\0';
  1518.     previous_save[0] = previous_save_[0] = '\0';
  1519.   memset(r, 0, sizeof(_PT_Element));
  1520.   r->location = location_default;
  1521.     strcpy(r->location, ""); 
  1522.     if (strncmp(url, "http://", 7) == 0)
  1523.         url += 7;
  1524.   hash_pos_return=inthash_read(cache->hash, url, &hash_pos);
  1525.  
  1526.   if (hash_pos_return) {
  1527.     int pos = (int) hash_pos;     /* simply */
  1528.  
  1529.     if (fseek(cache->dat, (pos>0) ? pos : (-pos), SEEK_SET) == 0) {
  1530.             /* Importer cache1.0 */
  1531.             if (cache->version==0) {
  1532.                 OLD_htsblk old_r;
  1533.                 if (fread((char*) &old_r,1,sizeof(old_r),cache->dat) == sizeof(old_r)) { // lire tout (y compris statuscode etc)
  1534.                     int i;
  1535.                     String urlDecoded;
  1536.                     r->statuscode = old_r.statuscode;
  1537.                     r->size = old_r.size;        // taille fichier
  1538.                     strcpy(r->msg, old_r.msg);
  1539.                     strcpy(r->contenttype, old_r.contenttype);
  1540.  
  1541.                     /* Guess the destination filename.. this sucks, because this method is not reliable.
  1542.                         Yes, the old 1.0 cache format was *that* bogus. /rx */
  1543. #define FORBIDDEN_CHAR(c) (c == '~' \
  1544.     || c == '\\' \
  1545.     || c == ':' \
  1546.     || c == '*' \
  1547.     || c == '?' \
  1548.     || c == '\"' \
  1549.     || c == '<' \
  1550.     || c == '>' \
  1551.     || c == '|' \
  1552.     || c == '@' \
  1553.     || ((unsigned char) c ) <= 31 \
  1554.     || ((unsigned char) c ) == 127 \
  1555.     )
  1556.                     urlDecoded = DecodeUrl(jump_protocol_and_auth(url));
  1557.                     strcpy(previous_save_, StringBuff(urlDecoded));
  1558.                     StringFree(urlDecoded);
  1559.                     for(i = 0 ; previous_save_[i] != '\0' && previous_save_[i] != '?' ; i++) {
  1560.                         if (FORBIDDEN_CHAR(previous_save_[i])) {
  1561.                             previous_save_[i] = '_';
  1562.                         }
  1563.                     }
  1564.                     previous_save_[i] = '\0';
  1565. #undef FORBIDDEN_CHAR
  1566.                 ok = 1;     /* import  ok */
  1567.             }
  1568.       /* */
  1569.       /* Cache 1.1 */
  1570.       } else {
  1571.         char check[256];
  1572.         unsigned long size_read;
  1573.         unsigned long int size_;
  1574.         check[0]='\0';
  1575.         //
  1576.         cache_rint(cache->dat,&r->statuscode);
  1577.         cache_rLLint(cache->dat,&size_);
  1578.         r->size = (size_t) size_;
  1579.         cache_rstr(cache->dat,r->msg);
  1580.         cache_rstr(cache->dat,r->contenttype);
  1581.         if (cache->version >= 3)
  1582.           cache_rstr(cache->dat,r->charset);
  1583.         cache_rstr(cache->dat,r->lastmodified);
  1584.         cache_rstr(cache->dat,r->etag);
  1585.         cache_rstr(cache->dat,r->location);
  1586.         if (cache->version >= 2)
  1587.           cache_rstr(cache->dat,r->cdispo);
  1588.         if (cache->version >= 4) {
  1589.           cache_rstr(cache->dat, previous_save_);  // adr
  1590.           cache_rstr(cache->dat, previous_save_);  // fil
  1591.           previous_save[0] = '\0';
  1592.           cache_rstr(cache->dat, previous_save_);  // save
  1593.         }
  1594.         if (cache->version >= 5) {
  1595.           r->headers = cache_rstr_addr(cache->dat);
  1596.         }
  1597.         //
  1598.         cache_rstr(cache->dat,check);
  1599.         if (strcmp(check,"HTS")==0) {           /* intΘgritΘ OK */
  1600.           ok=1;
  1601.         }
  1602.         cache_rLLint(cache->dat, &size_read);       /* lire size pour Ωtre s√r de la taille dΘclarΘe (rΘΘcrire) */
  1603.         if (size_read > 0) {                         /* si inscrite ici */
  1604.           r->size = size_read;
  1605.         } else {                              /* pas de donnΘes directement dans le cache, fichier prΘsent? */
  1606.                     r->size = 0;
  1607.         }
  1608.       }
  1609.  
  1610.             /* Check destination filename */
  1611.  
  1612.             {
  1613.                 PT_Index__Old index = cache;
  1614.                 /* -------------------- COPY OF THE __New() CODE -------------------- */
  1615.                 if (previous_save_[0] != '\0') {
  1616.                     int pathLen = (int) strlen(index->path);
  1617.                     if (pathLen > 0 && strncmp(previous_save_, index->path, pathLen) == 0) {            // old (<3.40) buggy format
  1618.                         strcpy(previous_save, previous_save_);
  1619.                     }
  1620.                     // relative ? (hack)
  1621.                     else if (index->safeCache
  1622.                         || (previous_save_[0] != '/'                                                                    // /home/foo/bar.gif
  1623.                         && ( !isalpha(previous_save_[0]) || previous_save_[1] != ':' ) )    // c:/home/foo/bar.gif
  1624.                         )
  1625.                     {
  1626.                         index->safeCache = 1;
  1627.                         sprintf(previous_save, "%s%s", index->path, previous_save_);
  1628.                     }
  1629.                     // bogus format (includes buggy absolute path)
  1630.                     else {
  1631.                         /* guess previous path */
  1632.                         if (index->fixedPath == 0) {
  1633.                             const char * start = jump_protocol_and_auth(url);
  1634.                             const char * end = start ? strchr(start, '/') : NULL;
  1635.                             int len = (int) (end - start);
  1636.                             if (start != NULL && end != NULL && len > 0 && len < 128) {
  1637.                                 char piece[128 + 2];
  1638.                                 const char * where;
  1639.                                 piece[0] = '\0';
  1640.                                 strncat(piece, start, len);
  1641.                                 if ((where = strstr(previous_save_, piece)) != NULL) {
  1642.                                     index->fixedPath = (int) (where - previous_save_);        // offset to relative path
  1643.                                 }
  1644.                             }
  1645.                         }
  1646.                         if (index->fixedPath > 0) {
  1647.                             int saveLen = (int) strlen(previous_save_);
  1648.                             if (index->fixedPath < saveLen) {
  1649.                                 sprintf(previous_save, "%s%s", index->path, previous_save_ + index->fixedPath);
  1650.                             } else {
  1651.                                 sprintf(r->msg, "Bogus fixePath prefix for %s (prefixLen=%d)", previous_save_, (int)index->fixedPath);
  1652.                                 r->statuscode = STATUSCODE_INVALID;
  1653.                             }
  1654.                         } else {
  1655.                             sprintf(previous_save, "%s%s", index->path, previous_save_);
  1656.                         }
  1657.                     }
  1658.                 }
  1659.                 /* -------------------- END OF COPY OF THE __New() CODE -------------------- */
  1660.             }
  1661.  
  1662.       /* Read data */
  1663.             if (ok) {
  1664.                 r->adr = NULL;
  1665.                 if ( (r->statuscode>=0) && (r->statuscode<=999)) {
  1666.                     r->adr = NULL;
  1667.                     if (pos<0) {
  1668.                         if (flags & FETCH_BODY) {
  1669.                             FILE* fp = fopen(previous_save, "rb");
  1670.                             if (fp != NULL) {
  1671.                                 r->adr = (char*) malloc(r->size + 1);
  1672.                                 if (r->adr != NULL) {
  1673.                                     if (r->size > 0 && fread(r->adr, 1, r->size, fp) != r->size) {
  1674.                                         r->statuscode=STATUSCODE_INVALID;
  1675.                                         strcpy(r->msg,"Read error in cache disk data");
  1676.                                     }
  1677.                                     r->adr[r->size] = '\0';
  1678.                                 } else {
  1679.                                     r->statuscode=STATUSCODE_INVALID;
  1680.                                     strcpy(r->msg,"Read error (memory exhausted) from cache");
  1681.                                 }
  1682.                                 fclose(fp);
  1683.                             } else {
  1684.                                 r->statuscode = STATUSCODE_INVALID;
  1685.                                 strcpy(r->msg, "Previous cache file not found (2)");
  1686.                             }
  1687.                         }
  1688.                     } else {
  1689.                         // lire fichier (d'un coup)
  1690.                         if (flags & FETCH_BODY) {
  1691.                             r->adr=(char*) malloc(r->size + 1);
  1692.                             if (r->adr!=NULL) {
  1693.                                 if (fread(r->adr, 1, r->size,cache->dat) != r->size) {  // erreur
  1694.                                     free(r->adr);
  1695.                                     r->adr=NULL;
  1696.                                     r->statuscode=STATUSCODE_INVALID;
  1697.                                     strcpy(r->msg,"Cache Read Error : Read Data");
  1698.                                 } else
  1699.                                     r->adr[r->size] = '\0';
  1700.                             } else {  // erreur
  1701.                                 r->statuscode=STATUSCODE_INVALID;
  1702.                                 strcpy(r->msg,"Cache Memory Error");
  1703.                             }
  1704.                         }
  1705.                     }
  1706.                 } else {
  1707.           r->statuscode=STATUSCODE_INVALID;
  1708.           strcpy(r->msg,"Cache Read Error : Bad Data");
  1709.         }
  1710.       } else {  // erreur
  1711.         r->statuscode=STATUSCODE_INVALID;
  1712.         strcpy(r->msg,"Cache Read Error : Read Header");
  1713.       }
  1714.     } else {
  1715.       r->statuscode=STATUSCODE_INVALID;
  1716.       strcpy(r->msg,"Cache Read Error : Seek Failed");
  1717.     }
  1718.   } else {
  1719.     r->statuscode=STATUSCODE_INVALID;
  1720.     strcpy(r->msg,"File Cache Entry Not Found");
  1721.   }
  1722.     if (r->location[0] != '\0') {
  1723.         r->location = strdup(r->location);
  1724.     } else {
  1725.         r->location = NULL;
  1726.     }
  1727.   return r;
  1728. }
  1729.  
  1730. static int PT_LookupCache__Old(PT_Index index, const char* url) {
  1731.     int retCode;
  1732.     MutexLock(&index->slots.formatOld.fileLock);
  1733.     {
  1734.         retCode = PT_LookupCache__Old_u(index, url);
  1735.     }
  1736.     MutexUnlock(&index->slots.formatOld.fileLock);
  1737.     return retCode;
  1738. }
  1739.  
  1740. static int PT_LookupCache__Old_u(PT_Index index_, const char* url) {
  1741.     if (index_ != NULL) {
  1742.         PT_Index__New cache = (PT_Index__New) &index_->slots.formatNew;
  1743.         if (cache == NULL || cache->hash == NULL || url == NULL || *url == 0)
  1744.             return 0;
  1745.         if (strncmp(url, "http://", 7) == 0)
  1746.             url += 7;
  1747.         if (inthash_read(cache->hash, url, NULL))
  1748.             return 1;
  1749.     }
  1750.     return 0;
  1751. }
  1752.  
  1753.  
  1754. /* ------------------------------------------------------------ */
  1755. /* Internet Archive Arc 1.0 (arc) format                        */
  1756. /* Xavier Roche (roche@httrack.com)                             */
  1757. /* Lars Clausen (lc@statsbiblioteket.dk)                        */
  1758. /* ------------------------------------------------------------ */
  1759.  
  1760. #define ARC_SP ' '
  1761.  
  1762. static const char* getArcField(const char *line, int pos) {
  1763.     int i;
  1764.     for(i = 0 ; line[i] != '\0' && pos > 0 ; i++) {
  1765.         if (line[i] == ARC_SP)
  1766.             pos--;
  1767.     }
  1768.     if (pos == 0)
  1769.         return &line[i];
  1770.     return NULL;
  1771. }
  1772.  
  1773. static char* copyArcField(const char *line, int npos, char *dest, int destMax) {
  1774.     const char *pos;
  1775.     if ((pos = getArcField(line, npos)) != NULL) {
  1776.         int i;
  1777.         for(i = 0 ; pos[i] != '\0' && pos[i] != ARC_SP && ( --destMax ) > 0; i++) {
  1778.             dest[i] = pos[i];
  1779.         }
  1780.         dest[i] = 0;
  1781.         return dest;
  1782.     }
  1783.     dest[0] = 0;
  1784.     return NULL;
  1785. }
  1786.  
  1787. static int getArcLength(const char *line) {
  1788.     const char *pos;
  1789.     if ((pos = getArcField(line, 9)) != NULL 
  1790.         || (pos = getArcField(line, 4)) != NULL 
  1791.         || (pos = getArcField(line, 2)) != NULL
  1792.         ) {
  1793.         int length;
  1794.         if (sscanf(pos, "%d", &length) == 1) {
  1795.             return length;
  1796.         }
  1797.     }
  1798.     return -1;
  1799. }
  1800.  
  1801. static int skipArcNl(FILE* file) {
  1802.     if (fgetc(file) == 0x0a) {
  1803.         return 0;
  1804.     }
  1805.     return -1;
  1806. }
  1807.  
  1808. static int skipArcData(FILE* file, const char *line) {
  1809.     int jump = getArcLength(line);
  1810.     if (jump != -1) {
  1811.         if (fseek(file, jump, SEEK_CUR) == 0 /* && skipArcNl(file) == 0 */) {
  1812.             return 0;
  1813.         }
  1814.     }
  1815.     return -1;
  1816. }
  1817.  
  1818. static int getDigit(const char digit) {
  1819.     return (int) ( digit - '0' );
  1820. }
  1821.  
  1822. static int getDigit2(const char * const pos) {
  1823.     return getDigit(pos[0])*10 + getDigit(pos[1]);
  1824. }
  1825.  
  1826. static int getDigit4(const char * const pos) {
  1827.     return getDigit(pos[0])*1000 + getDigit(pos[1])*100 + getDigit(pos[2])*10 + getDigit(pos[3]);
  1828. }
  1829.  
  1830. static time_t getGMT(struct tm *tm) {        /* hey, time_t is local! */
  1831.     time_t t = mktime(tm);
  1832.     if (t != (time_t) -1 && t != (time_t) 0) {
  1833.     /* BSD does not have static "timezone" declared */
  1834. #if (defined(BSD) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) || defined(__FreeBSD_kernel__))
  1835.     time_t now = time(NULL); 
  1836.     time_t timezone = - localtime(&now)->tm_gmtoff;
  1837. #endif
  1838.         return (time_t) (t - timezone);
  1839.     }
  1840.     return (time_t) -1;
  1841. }
  1842.  
  1843. static time_t getArcTimestamp(const char * const line) {
  1844.     const char *pos;
  1845.     if ((pos = getArcField(line, 2)) != NULL) {
  1846.         int i;
  1847.         /* date == YYYYMMDDhhmmss (Greenwich Mean Time) */
  1848.         /* example: 20050405154029  */
  1849.         for(i = 0 ; pos[i] >= '0' && pos[i] <= '9' ; i++);
  1850.         if (i == 14) {
  1851.             struct tm tm;
  1852.             memset(&tm, 0, sizeof(tm));
  1853.             tm.tm_year = getDigit4(pos + 0) - 1900;    /* current year minus 1900 */
  1854.             tm.tm_mon =  getDigit2(pos + 4) - 1;        /* 0 û 11 */
  1855.             tm.tm_mday = getDigit2(pos + 6);                /* 1 û 31 */
  1856.             tm.tm_hour = getDigit2(pos + 8);                /* 0 û 23 */
  1857.             tm.tm_min =  getDigit2(pos + 10);                /* 0 û 59 */
  1858.             tm.tm_sec =  getDigit2(pos + 12);                /* 0 û 59 */
  1859.             tm.tm_isdst = 0;
  1860.             return getGMT(&tm);
  1861.         }
  1862.     }
  1863.     return (time_t) -1;
  1864. }
  1865.  
  1866. static int readArcURLRecord(PT_Index__Arc index) {
  1867.     index->line[0] = '\0';
  1868.     if (linput(index->file, index->line, sizeof(index->line) - 1)) {
  1869.         return 0;
  1870.     }
  1871.     return -1;
  1872. }
  1873.  
  1874. #define str_begins(str, sstr) ( strncmp(str, sstr, sizeof(sstr) - 1) == 0 )
  1875. static int PT_CompatibleScheme(const char *url) {
  1876.     return (str_begins(url, "http:") 
  1877.         || str_begins(url, "https:") 
  1878.         || str_begins(url, "ftp:") 
  1879.         || str_begins(url, "file:"));
  1880. }
  1881.  
  1882. int PT_LoadCache__Arc(PT_Index index_, const char *filename) {
  1883.     if (index_ != NULL && filename != NULL) {
  1884.         PT_Index__Arc index = &index_->slots.formatArc;
  1885.         index->timestamp = file_timestamp(filename);
  1886.         MutexInit(&index->fileLock);
  1887.         index->file = fopen(filename, "rb");
  1888.  
  1889.         // Opened ?
  1890.         if (index->file != NULL) {
  1891.             inthash hashtable = index->hash;
  1892.             if (readArcURLRecord(index) == 0) {
  1893.                 int entries = 0;
  1894.                 /* Read first line */
  1895.                 if (strncmp(index->line, "filedesc://", sizeof("filedesc://") - 1) != 0) {
  1896.                     fprintf(stderr, "Unexpected bad signature #%s"LF, index->line);
  1897.                     fclose(index->file);
  1898.                     index->file = NULL;
  1899.                     return 0;
  1900.                 }
  1901.                 /* Timestamp */
  1902.                 index->timestamp = getArcTimestamp(index->line);
  1903.                 /* Skip first entry */
  1904.                 if (skipArcData(index->file, index->line) != 0 || skipArcNl(index->file) != 0) {
  1905.                     fprintf(stderr, "Unexpected bad data offset size first entry"LF);
  1906.                     fclose(index->file);
  1907.                     index->file = NULL;
  1908.                     return 0;
  1909.                 }
  1910.                 /* Read all meta-entries (not data) */
  1911.                 while(!feof(index->file)) {
  1912.                     unsigned long int fpos = ftell(index->file);
  1913.                     if (skipArcNl(index->file) == 0 && readArcURLRecord(index) == 0) {
  1914.                         int length = getArcLength(index->line);
  1915.                         if (length >= 0) {
  1916.                             const char * filenameIndex = copyArcField(index->line, 0, 
  1917.                                 index->filenameIndexBuff, sizeof(index->filenameIndexBuff) - 1);  /* can not be NULL */
  1918.                             if (strncmp(filenameIndex, "http://", 7) == 0) {
  1919.                                 filenameIndex += 7;
  1920.                             }
  1921.                             if (*filenameIndex != 0) {
  1922.                                 if (skipArcData(index->file, index->line) != 0) {
  1923.                                     fprintf(stderr, "Corrupted cache data entry #%d (truncated file?), aborting read"LF, (int)entries);
  1924.                                 }
  1925.                                 /*fprintf(stdout, "adding %s [%d]\n", filenameIndex, (int)fpos);*/
  1926.                                 if (PT_CompatibleScheme(index->filenameIndexBuff)) {
  1927.                                     inthash_add(hashtable, filenameIndex, fpos);        /* position of meta-data */
  1928.                                     entries++;
  1929.                                 }
  1930.                             } else {
  1931.                                 fprintf(stderr, "Corrupted cache meta entry #%d"LF, (int)entries);
  1932.                             }
  1933.                         } else {
  1934.                             fprintf(stderr, "Corrupted cache meta entry #%d, aborting read"LF, (int)entries);
  1935.                             break ;
  1936.                         }
  1937.                     } else {
  1938.                         break ;
  1939.                     }
  1940.                 }
  1941.  
  1942.                 /* OK */
  1943.                 return 1;
  1944.             } else {
  1945.                 fprintf(stderr, "Bad file (empty ?)"LF);
  1946.             }
  1947.         } else {
  1948.             fprintf(stderr, "Unable to open file"LF);
  1949.             index = NULL;
  1950.         }
  1951.     } else {
  1952.         fprintf(stderr, "Bad arguments"LF);
  1953.     }
  1954.     return 0;
  1955. }
  1956.  
  1957. #define HTTP_READFIELD_STRING(line, value, refline, refvalue) do { \
  1958.   if (line[0] != '\0' && strfield2(line, refline)) { \
  1959.     strcpy(refvalue, value); \
  1960.     line[0] = '\0'; \
  1961.     } \
  1962. } while(0)
  1963. #define HTTP_READFIELD_INT(line, value, refline, refvalue) do { \
  1964.   if (line[0] != '\0' && strfield2(line, refline)) { \
  1965.     int intval = 0; \
  1966.     sscanf(value, "%d", &intval); \
  1967.     (refvalue) = intval; \
  1968.     line[0] = '\0'; \
  1969.     } \
  1970. } while(0)
  1971.  
  1972. static PT_Element PT_ReadCache__Arc(PT_Index index, const char* url, int flags) {
  1973.     PT_Element retCode;
  1974.     MutexLock(&index->slots.formatArc.fileLock);
  1975.     {
  1976.         retCode = PT_ReadCache__Arc_u(index, url, flags);
  1977.     }
  1978.     MutexUnlock(&index->slots.formatArc.fileLock);
  1979.     return retCode;
  1980. }
  1981.  
  1982. static PT_Element PT_ReadCache__Arc_u(PT_Index index_, const char* url, int flags)
  1983. {
  1984.     PT_Index__Arc index = (PT_Index__Arc) &index_->slots.formatArc;
  1985.     char location_default[HTS_URLMAXSIZE*2];
  1986.   intptr_t hash_pos;
  1987.   int hash_pos_return;
  1988.     PT_Element r = NULL;
  1989.     if (index == NULL || index->hash == NULL || url == NULL || *url == 0)
  1990.         return NULL;
  1991.     if ((r = PT_ElementNew()) == NULL)
  1992.         return NULL;
  1993.     location_default[0] = '\0';
  1994.   memset(r, 0, sizeof(_PT_Element));
  1995.   r->location = location_default;
  1996.     strcpy(r->location, ""); 
  1997.     if (strncmp(url, "http://", 7) == 0)
  1998.         url += 7;
  1999.   hash_pos_return = inthash_read(index->hash, url, &hash_pos);
  2000.  
  2001.     if (hash_pos_return) {
  2002.         if (fseek(index->file, (long)hash_pos, SEEK_SET) == 0) {
  2003.             if (skipArcNl(index->file) == 0 && readArcURLRecord(index) == 0) {
  2004.                 long int fposMeta = ftell(index->file);
  2005.                 int dataLength = getArcLength(index->line);
  2006.                 const char *pos;
  2007.  
  2008.                 /* Read HTTP headers */
  2009.                 /* HTTP/1.1 404 Not Found */
  2010.                 if (linput(index->file, index->line, sizeof(index->line) - 1)) {
  2011.                     if ((pos = getArcField(index->line, 1)) != NULL) {
  2012.                         if (sscanf(pos, "%d", &r->statuscode) != 1) {
  2013.                             r->statuscode = STATUSCODE_INVALID;
  2014.                         }
  2015.                     }
  2016.                     if ((pos = getArcField(index->line, 2)) != NULL) {
  2017.                         r->msg[0] = '\0';
  2018.                         strncat(r->msg, pos, sizeof(pos) - 1);
  2019.                     }
  2020.                     while (linput(index->file, index->line, sizeof(index->line) - 1) && index->line[0] != '\0') {
  2021.                         char* const line = index->line;
  2022.                         char* value = strchr(line, ':');
  2023.                         if (value != NULL) {
  2024.                             *value = '\0';
  2025.                             for( value++ ; *value == ' ' || *value == '\t' ; value++);
  2026.                             HTTP_READFIELD_INT(line, value, "Content-Length", r->size);           // size
  2027.                             HTTP_READFIELD_STRING(line, value, "Content-Type", r->contenttype);      // contenttype
  2028.                             HTTP_READFIELD_STRING(line, value, "Last-Modified", r->lastmodified);     // last-modified
  2029.                             HTTP_READFIELD_STRING(line, value, "Etag", r->etag);             // Etag
  2030.                             HTTP_READFIELD_STRING(line, value, "Location", r->location);         // 'location' pour moved
  2031.                             HTTP_READFIELD_STRING(line, value, "Content-Disposition", r->cdispo);           // Content-disposition
  2032.                             if (line[0] != '\0') {
  2033.                                 int len = r->headers ? ((int) strlen(r->headers)) : 0;
  2034.                                 int nlen = (int) ( strlen(line) + 2 + strlen(value) + sizeof("\r\n") + 1 );
  2035.                                 r->headers = realloc(r->headers, len + nlen);
  2036.                                 r->headers[len] = '\0';
  2037.                                 strcat(r->headers, line);
  2038.                                 strcat(r->headers, ": ");
  2039.                                 strcat(r->headers, value);
  2040.                                 strcat(r->headers, "\r\n");
  2041.                             }
  2042.                         }
  2043.                     }
  2044.  
  2045.                     /* FIXME charset */
  2046.                     if (r->contenttype[0] != '\0') {
  2047.                         char *pos = strchr(r->contenttype, ';');
  2048.                         if (pos != NULL) {
  2049.                             /*char *chs = strchr(pos, "charset=");*/
  2050.                             /*HTTP_READFIELD_STRING(line, value, "X-Charset", r->charset);*/
  2051.                             *pos = 0;
  2052.                             if ((pos = strchr(r->contenttype, ' ')) != NULL) {
  2053.                                 *pos = 0;
  2054.                             }
  2055.                         }
  2056.                     }
  2057.  
  2058.                     /* Read data */
  2059.                     if (r->statuscode != STATUSCODE_INVALID) {            /* Can continue */
  2060.                         if (flags & FETCH_BODY) {
  2061.                             long int fposCurrent = ftell(index->file);
  2062.                             long int metaSize = fposCurrent - fposMeta;
  2063.                             long int fetchSize = (long int) r->size;
  2064.                             if (fetchSize <= 0) {
  2065.                                 fetchSize = dataLength - metaSize;
  2066.                             } else if (fetchSize > dataLength - metaSize) {
  2067.                                 r->statuscode=STATUSCODE_INVALID;
  2068.                                 strcpy(r->msg, "Cache Read Error : Truncated Data");
  2069.                             }
  2070.                             r->size = 0;
  2071.                             if (r->statuscode != STATUSCODE_INVALID) {
  2072.                                 r->adr = (char*) malloc(fetchSize);
  2073.                                 if (r->adr != NULL) {
  2074.                                     if (fetchSize > 0 && ( r->size = (int) fread(r->adr, 1,  fetchSize, index->file) ) != fetchSize) {
  2075.                     int last_errno = errno;
  2076.                                         r->statuscode=STATUSCODE_INVALID;
  2077.                                         sprintf(r->msg,"Read error in cache disk data: %s", strerror(last_errno));
  2078.                                     }
  2079.                                 } else {
  2080.                                     r->statuscode=STATUSCODE_INVALID;
  2081.                                     strcpy(r->msg,"Read error (memory exhausted) from cache");
  2082.                                 }
  2083.                             }
  2084.                         }
  2085.                     }
  2086.  
  2087.                 } else {
  2088.                     r->statuscode=STATUSCODE_INVALID;
  2089.                     strcpy(r->msg, "Cache Read Error : Read Header Error");
  2090.                 }
  2091.  
  2092.             } else {
  2093.                 r->statuscode=STATUSCODE_INVALID;
  2094.                 strcpy(r->msg, "Cache Read Error : Read Header Error");
  2095.             }
  2096.         } else {
  2097.             r->statuscode=STATUSCODE_INVALID;
  2098.             strcpy(r->msg, "Cache Read Error : Seek Error");
  2099.         }
  2100.  
  2101.     } else {
  2102.         r->statuscode=STATUSCODE_INVALID;
  2103.     strcpy(r->msg,"File Cache Entry Not Found");
  2104.   }
  2105.     if (r->location[0] != '\0') {
  2106.         r->location = strdup(r->location);
  2107.     } else {
  2108.         r->location = NULL;
  2109.     }
  2110.   return r;
  2111. }
  2112.  
  2113. static int PT_LookupCache__Arc(PT_Index index, const char* url) {
  2114.     int retCode;
  2115.     MutexLock(&index->slots.formatArc.fileLock);
  2116.     {
  2117.         retCode = PT_LookupCache__Arc_u(index, url);
  2118.     }
  2119.     MutexUnlock(&index->slots.formatArc.fileLock);
  2120.     return retCode;
  2121. }
  2122.  
  2123. static int PT_LookupCache__Arc_u(PT_Index index_, const char* url) {
  2124.     if (index_ != NULL) {
  2125.         PT_Index__New cache = (PT_Index__New) &index_->slots.formatNew;
  2126.         if (cache == NULL || cache->hash == NULL || url == NULL || *url == 0)
  2127.             return 0;
  2128.         if (strncmp(url, "http://", 7) == 0)
  2129.             url += 7;
  2130.         if (inthash_read(cache->hash, url, NULL))
  2131.             return 1;
  2132.     }
  2133.     return 0;
  2134. }
  2135.  
  2136. typedef struct PT_SaveCache__Arc_t {
  2137.     PT_Indexes indexes;
  2138.     FILE *fp;
  2139.     time_t t;
  2140.     char filename[64];
  2141.     struct tm buff;
  2142.     char headers[8192];
  2143.   char md5[32 + 2];
  2144. } PT_SaveCache__Arc_t;
  2145.  
  2146. static int PT_SaveCache__Arc_Fun(void *arg, const char *url, PT_Element element) {
  2147.     PT_SaveCache__Arc_t *st = (PT_SaveCache__Arc_t*) arg;
  2148.     FILE * const fp = st->fp;
  2149.     struct tm* tm = convert_time_rfc822(&st->buff, element->lastmodified);
  2150.     int size_headers;
  2151.  
  2152.     sprintf(st->headers, 
  2153.         "HTTP/1.0 %d %s" "\r\n"
  2154.         "X-Server: ProxyTrack " PROXYTRACK_VERSION "\r\n"
  2155.         "Content-type: %s%s%s%s" "\r\n"
  2156.         "Last-modified: %s" "\r\n"
  2157.         "Content-length: %d" "\r\n"
  2158.         ,
  2159.         element->statuscode, element->msg,
  2160.         /**/
  2161.         element->contenttype, 
  2162.         (element->charset[0] ? "; charset=\"" : ""), 
  2163.         (element->charset[0] ? element->charset : ""), 
  2164.         (element->charset[0] ? "\"" : ""),
  2165.         /**/
  2166.         element->lastmodified,
  2167.         (int) element->size
  2168.         );
  2169.     if (element->location != NULL && element->location[0] != '\0') {
  2170.         sprintf(st->headers + strlen(st->headers), "Location: %s" "\r\n", element->location);
  2171.     }
  2172.     if (element->headers != NULL) {
  2173.         if ( strlen(element->headers) < sizeof(st->headers) - strlen(element->headers) - 1 ) {
  2174.             strcat(st->headers, element->headers);
  2175.         }
  2176.     }
  2177.     strcat(st->headers, "\r\n");
  2178.     size_headers = (int) strlen(st->headers);
  2179.  
  2180.     /* doc == <nl><URL-record><nl><network_doc> */
  2181.  
  2182.   /* Format: URL IP date mime result checksum location offset filename length */
  2183.     if (element->adr != NULL) {
  2184.         domd5mem(element->adr, element->size, st->md5, 1);
  2185.     } else {
  2186.         strcpy(st->md5, "-");
  2187.     }
  2188.   fprintf(fp, 
  2189.         /* nl */
  2190.         "\n"
  2191.       /* URL-record */
  2192.         "%s%s %s %04d%02d%02d%02d%02d%02d %s %d %s %s %ld %s %ld"
  2193.         /* nl */
  2194.         "\n",
  2195.         /* args */
  2196.       ( link_has_authority(url) ? "" : "http://" ), url,
  2197.         "0.0.0.0",
  2198.         tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
  2199.       element->contenttype,
  2200.       element->statuscode,
  2201.         st->md5, ( element->location ? element->location : "-" ),
  2202.       (long int)ftell(fp), st->filename,
  2203.       (long int)( size_headers + element->size ));
  2204.     /* network_doc */
  2205.     if (fwrite(st->headers, 1, size_headers, fp) != size_headers
  2206.             || ( element->size > 0 && fwrite(element->adr, 1, element->size, fp) != element->size )
  2207.         ) {
  2208.         return 1;            /* Error */
  2209.     }
  2210.  
  2211.     return 0;
  2212. }
  2213.  
  2214. static int PT_SaveCache__Arc(PT_Indexes indexes, const char *filename) {
  2215.     FILE *fp = fopen(filename, "wb");
  2216.     if (fp != NULL) {
  2217.         PT_SaveCache__Arc_t st;
  2218.         int ret;
  2219.         time_t t = PT_GetTimeIndex(indexes);
  2220.         struct tm tm = PT_GetTime(t);
  2221.  
  2222.         /* version-2-block ==
  2223.         filedesc://<path><sp><ip_address><sp><date><sp>text/plain<sp>200<sp>-<sp>-<sp>0<sp><filename><sp><length><nl>
  2224.         2<sp><reserved><sp><origin-code><nl>
  2225.         URL<sp>IP-address<sp>Archive-date<sp>Content-type<sp>Result-code<sp>Checksum<sp>Location<sp> Offset<sp>Filename<sp>Archive-length<nl>
  2226.         <nl> */
  2227.         const char* prefix = 
  2228.             "2 0 HTTrack Website Copier" "\n"
  2229.             "URL IP-address Archive-Date Content-Type Result-code Checksum Location Offset Filename Archive-length" "\n" "\n";
  2230.         sprintf(st.filename, "httrack_%d.arc", (int) t);
  2231.         fprintf(fp, "filedesc://%s 0.0.0.0 %04d%02d%02d%02d%02d%02d text/plain 200 - - 0 %s %d" "\n"
  2232.             "%s",
  2233.             st.filename, 
  2234.             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec,
  2235.             st.filename, (int)strlen(prefix), prefix);
  2236.         st.fp = fp;
  2237.         st.indexes = indexes;
  2238.         st.t = t;
  2239.         ret = PT_EnumCache(indexes, PT_SaveCache__Arc_Fun, (void *)&st);
  2240.         fclose(fp);
  2241.         if (ret != 0)
  2242.             (void) unlink(filename);
  2243.         return ret;
  2244.     }
  2245.     return -1;
  2246. }
  2247.